/*
 * Tint Browser for Android
 * 
 * Copyright (C) 2012 - to infinity and beyond J. Devauchelle and contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package com.seal.model;

import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.DateSorter;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.TextView;

import com.seal.R;
import com.seal.providers.BookmarksProvider;

/**
 * Custom adapter for displaying history, splitted in bins. Adapted from: https://github.com/CyanogenMod/android_packages_apps_Browser/blob/gingerbread/src/com/android/browser/BrowserHistoryPage.java http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/2.2_r1.1/com/android/browser/DateSortedExpandableListAdapter.java/?v=source
 */
public class HistoryAdapter extends BaseExpandableListAdapter {

	private LayoutInflater mInflater = null;

	private int[] mItemMap;
	private int mNumberOfBins;
	private DateSorter mDateSorter;
	private int mIdIndex;

	private Context mContext;
	private Cursor mCursor;

	private int mDateIndex;

	private int mFaviconSize;

	private OnCheckedChangeListener mBookmarkStarChangeListener;

	/**
	 * Constructor.
	 * 
	 * @param context
	 *            The current context.
	 * @param cursor
	 *            The data cursor.
	 * @param dateIndex
	 *            The date index ?
	 */
	public HistoryAdapter(Context context, OnCheckedChangeListener bookmarksChangeListener, int faviconSize) {
		mContext = context;
		mBookmarkStarChangeListener = bookmarksChangeListener;
		mCursor = null;
		mDateIndex = -1;
		mFaviconSize = faviconSize;

		mDateSorter = new DateSorter(mContext);

		mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}

	public void changeCursor(Cursor cursor) {
		if (mCursor == cursor) {
			return;
		}

		mCursor = cursor;

		if (mCursor != null) {
			mIdIndex = mCursor.getColumnIndexOrThrow(BookmarksProvider.Columns._ID);
			mDateIndex = mCursor.getColumnIndexOrThrow(BookmarksProvider.Columns.VISITED_DATE);

			buildMap();

			notifyDataSetChanged();
		} else {
			mIdIndex = -1;
			mDateIndex = -1;

			notifyDataSetInvalidated();
		}
	}

	/**
	 * Get a long-typed data from mCursor.
	 * 
	 * @param cursorIndex
	 *            The column index.
	 * @return The long data.
	 */
	private long getLong(int cursorIndex) {
		return mCursor.getLong(cursorIndex);
	}

	/**
	 * Split the data in the cursor into several "bins": today, yesterday, last 7 days, last month, older.
	 */
	private void buildMap() {
		int[] array = new int[DateSorter.DAY_COUNT];
		// Zero out the array.
		for (int j = 0; j < DateSorter.DAY_COUNT; j++) {
			array[j] = 0;
		}

		mNumberOfBins = 0;
		int dateIndex = -1;
		if (mCursor.moveToFirst() && mCursor.getCount() > 0) {
			while (!mCursor.isAfterLast()) {
				long date = getLong(mDateIndex);
				int index = mDateSorter.getIndex(date);
				if (index > dateIndex) {
					mNumberOfBins++;
					if (index == DateSorter.DAY_COUNT - 1) {
						// We are already in the last bin, so it will
						// include all the remaining items
						array[index] = mCursor.getCount() - mCursor.getPosition();
						break;
					}
					dateIndex = index;
				}
				array[dateIndex]++;
				mCursor.moveToNext();
			}
		}

		mItemMap = array;
	}

	/**
	 * Translates from a group position in the ExpandableList to a bin. This is necessary because some groups have no history items, so we do not include those in the ExpandableList.
	 * 
	 * @param groupPosition
	 *            Position in the ExpandableList's set of groups
	 * @return The corresponding bin that holds that group.
	 */
	private int groupPositionToBin(int groupPosition) {
		if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) {
			throw new AssertionError("group position out of range");
		}

		if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) {
			// In the first case, we have exactly the same number of bins
			// as our maximum possible, so there is no need to do a
			// conversion
			// The second statement is in case this method gets called when
			// the array is empty, in which case the provided groupPosition
			// will do fine.
			return groupPosition;
		}

		int arrayPosition = -1;
		while (groupPosition > -1) {
			arrayPosition++;
			if (mItemMap[arrayPosition] != 0) {
				groupPosition--;
			}
		}

		return arrayPosition;
	}

	/**
	 * Move the cursor to the record corresponding to the given group position and child position.
	 * 
	 * @param groupPosition
	 *            The group position.
	 * @param childPosition
	 *            The child position.
	 * @return True if the move has succeeded.
	 */
	private boolean moveCursorToChildPosition(int groupPosition, int childPosition) {
		if (mCursor.isClosed()) {
			return false;
		}

		groupPosition = groupPositionToBin(groupPosition);
		int index = childPosition;
		for (int i = 0; i < groupPosition; i++) {
			index += mItemMap[i];
		}

		return mCursor.moveToPosition(index);
	}

	/**
	 * Create a new child view.
	 * 
	 * @return The created view.
	 */
	private View getCustomChildView() {
		return mInflater.inflate(R.layout.history_row, null, false);
	}

	@Override
	public Object getChild(int groupPosition, int childPosition) {
		moveCursorToChildPosition(groupPosition, childPosition);

		return new BookmarkHistoryItem(mCursor.getLong(mCursor.getColumnIndex(BookmarksProvider.Columns._ID)), mCursor.getString(mCursor
				.getColumnIndex(BookmarksProvider.Columns.TITLE)), mCursor.getString(mCursor.getColumnIndex(BookmarksProvider.Columns.URL)),
				mCursor.getInt(mCursor.getColumnIndex(BookmarksProvider.Columns.BOOKMARK)) >= 1 ? true : false, mCursor.getInt(mCursor
						.getColumnIndex(BookmarksProvider.Columns.IS_FOLDER)) >= 1 ? true : false, mCursor.getLong(mCursor
						.getColumnIndex(BookmarksProvider.Columns.PARENT_FOLDER_ID)),
				mCursor.getBlob(mCursor.getColumnIndex(BookmarksProvider.Columns.FAVICON)));
	}

	@Override
	public long getChildId(int groupPosition, int childPosition) {
		if (moveCursorToChildPosition(groupPosition, childPosition)) {
			return getLong(mIdIndex);
		}

		return 0;
	}

	@Override
	public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
		View view = getCustomChildView();

		TextView titleView = (TextView) view.findViewById(R.id.HistoryRow_Title);

		BookmarkHistoryItem item = (BookmarkHistoryItem) getChild(groupPosition, childPosition);
		titleView.setText(item.getTitle());

		TextView urlView = (TextView) view.findViewById(R.id.HistoryRow_Url);
		urlView.setText(item.getUrl());

		CheckBox bookmarkStar = (CheckBox) view.findViewById(R.id.HistoryRow_BookmarkStar);

		bookmarkStar.setTag(item.getId());

		bookmarkStar.setOnCheckedChangeListener(null);
		bookmarkStar.setChecked(item.isBookmark());
		bookmarkStar.setOnCheckedChangeListener(mBookmarkStarChangeListener);

		ImageView faviconView = (ImageView) view.findViewById(R.id.HistoryRow_Thumbnail);
		Bitmap favicon = item.getFavicon();
		if (favicon != null) {
			BitmapDrawable icon = new BitmapDrawable(view.getResources(), favicon);

			Bitmap bm = Bitmap.createBitmap(mFaviconSize, mFaviconSize, Bitmap.Config.ARGB_8888);
			Canvas canvas = new Canvas(bm);

			icon.setBounds(0, 0, mFaviconSize, mFaviconSize);
			icon.draw(canvas);

			faviconView.setImageBitmap(bm);
		} else {
			faviconView.setImageResource(R.drawable.app_web_browser_sm);
		}

		return view;
	}

	@Override
	public int getChildrenCount(int groupPosition) {
		if (mItemMap != null) {
			return mItemMap[groupPositionToBin(groupPosition)];
		} else {
			return 0;
		}
	}

	@Override
	public Object getGroup(int groupPosition) {
		int binIndex = groupPositionToBin(groupPosition);

		switch (binIndex) {
		case 0:
			return mContext.getResources().getString(R.string.HistoryToday);
		case 1:
			return mContext.getResources().getString(R.string.HistoryYesterday);
		case 2:
			return mContext.getResources().getString(R.string.HistoryLastSevenDays);
		case 3:
			return mContext.getResources().getString(R.string.HistoryLastMonth);
		default:
			return mContext.getResources().getString(R.string.HistoryOlder);
		}
	}

	@Override
	public int getGroupCount() {
		return mNumberOfBins;
	}

	@Override
	public long getGroupId(int groupPosition) {
		return groupPosition;
	}

	@Override
	public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {

		TextView item;
		if ((convertView == null) || (!(convertView instanceof TextView))) {
			LayoutInflater factory = LayoutInflater.from(mContext);
			item = (TextView) factory.inflate(R.layout.expandable_list_header, null);
		} else {
			item = (TextView) convertView;
		}

		item.setText(getGroup(groupPosition).toString());

		return item;
	}

	@Override
	public boolean hasStableIds() {
		return true;
	}

	@Override
	public boolean isChildSelectable(int groupPosition, int childPosition) {
		return true;
	}

}
